@@ -4,16 +4,26 @@ from __future__ import division  | 
            ||
| 4 | 4 | 
                 | 
            
| 5 | 5 | 
                import random  | 
            
| 6 | 6 | 
                 | 
            
| 7 | 
                +from django.conf import settings  | 
            |
| 8 | 
                +from django.db import transaction  | 
            |
| 7 | 9 | 
                from django_logit import logit  | 
            
| 8 | 10 | 
                from django_response import response  | 
            
| 11 | 
                +from pywe_marketcode import tickettocode  | 
            |
| 12 | 
                +from pywe_storage import RedisStorage  | 
            |
| 9 | 13 | 
                 | 
            
| 14 | 
                +from account.models import UserInfo  | 
            |
| 10 | 15 | 
                from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo  | 
            
| 16 | 
                +from marketcode.models import MarketCodeInfo  | 
            |
| 11 | 17 | 
                from mch.models import ActivityInfo, BrandInfo, ModelInfo  | 
            
| 12 | 18 | 
                from utils.algorithm.b64 import b64_decrypt, b64_encrypt  | 
            
| 13 | 19 | 
                from utils.algorithm.caesar import caesar_decrypt, caesar_encrypt  | 
            
| 14 | 20 | 
                from utils.algorithm.rsalg import rsa_decrypt, rsa_encrypt  | 
            
| 21 | 
                +from utils.error.errno_utils import MarketCodeStatusCode, UserStatusCode  | 
            |
| 22 | 
                +from utils.redis.connect import r  | 
            |
| 15 | 23 | 
                 | 
            
| 16 | 24 | 
                 | 
            
| 25 | 
                +WECHAT = settings.WECHAT  | 
            |
| 26 | 
                +  | 
            |
| 17 | 27 | 
                 # CIPHER_ALGORITHM = ('CAESAR', 'B64', 'RSA')
               | 
            
| 18 | 28 | 
                 CIPHER_ALGORITHM = ('CAESAR', )
               | 
            
| 19 | 29 | 
                 | 
            
                @@ -35,6 +45,30 @@ def encrypt(request):  | 
            ||
| 35 | 45 | 
                 | 
            
| 36 | 46 | 
                mieli, created_at = MchInfoEncryptLogInfo.objects.get_or_create(plaintext=plaintext)  | 
            
| 37 | 47 | 
                 | 
            
| 48 | 
                + if settings.KODO_MARKET_CODE_ENABLED:  | 
            |
| 49 | 
                + if created_at or not mieli.code:  | 
            |
| 50 | 
                + with transaction.atomic():  | 
            |
| 51 | 
                + marketcode = MarketCodeInfo.objects.select_for_update().filter(has_used=False).first()  | 
            |
| 52 | 
                +  | 
            |
| 53 | 
                + if not marketcode:  | 
            |
| 54 | 
                + return response(MarketCodeStatusCode.MARKET_CODE_NOT_FOUND)  | 
            |
| 55 | 
                +  | 
            |
| 56 | 
                + marketcode.has_used = True  | 
            |
| 57 | 
                + marketcode.save()  | 
            |
| 58 | 
                +  | 
            |
| 59 | 
                + mieli.code = marketcode.code  | 
            |
| 60 | 
                + mieli.code_url = marketcode.code_url  | 
            |
| 61 | 
                + mieli.brand_pk = brand_pk  | 
            |
| 62 | 
                + mieli.model_pk = model_pk  | 
            |
| 63 | 
                + mieli.distributor_pk = distributor_pk  | 
            |
| 64 | 
                + mieli.sn = sn  | 
            |
| 65 | 
                + mieli.operator_id = optor_id  | 
            |
| 66 | 
                + mieli.save()  | 
            |
| 67 | 
                +  | 
            |
| 68 | 
                +        return response(200, data={
               | 
            |
| 69 | 
                + 'ciphertext': mieli.code_url,  | 
            |
| 70 | 
                + })  | 
            |
| 71 | 
                +  | 
            |
| 38 | 72 | 
                if created_at:  | 
            
| 39 | 73 | 
                alg = random.choice(CIPHER_ALGORITHM)  | 
            
| 40 | 74 | 
                 | 
            
                @@ -160,3 +194,83 @@ def decrypt(request):  | 
            ||
| 160 | 194 | 
                         'redpack_info': elog.redpack_info if elog else {},
               | 
            
| 161 | 195 | 
                'integral': model.integral,  | 
            
| 162 | 196 | 
                })  | 
            
| 197 | 
                +  | 
            |
| 198 | 
                +  | 
            |
| 199 | 
                +@logit(res=True)  | 
            |
| 200 | 
                +def decrypt2(request):  | 
            |
| 201 | 
                +    code_ticket = request.POST.get('code_ticket', '')
               | 
            |
| 202 | 
                +    user_id = request.POST.get('user_id', '')
               | 
            |
| 203 | 
                +  | 
            |
| 204 | 
                + try:  | 
            |
| 205 | 
                + user = UserInfo.objects.get(user_id=user_id)  | 
            |
| 206 | 
                + except UserInfo.DoesNotExist:  | 
            |
| 207 | 
                + return response(UserStatusCode.USER_NOT_FOUND)  | 
            |
| 208 | 
                +  | 
            |
| 209 | 
                +    wxcfg = WECHAT.get('JSAPI', {})
               | 
            |
| 210 | 
                +  | 
            |
| 211 | 
                +    appid = wxcfg.get('appID')
               | 
            |
| 212 | 
                +    secret = wxcfg.get('appsecret')
               | 
            |
| 213 | 
                +  | 
            |
| 214 | 
                + code_info = tickettocode(code_ticket=code_ticket, openid=user.openid_miniapp, appid=appid, secret=secret, token=None, storage=RedisStorage(r))  | 
            |
| 215 | 
                +  | 
            |
| 216 | 
                +    code = code_info.get('code', '')
               | 
            |
| 217 | 
                +  | 
            |
| 218 | 
                + try:  | 
            |
| 219 | 
                + miel = MchInfoEncryptLogInfo.objects.get(code=code)  | 
            |
| 220 | 
                + except MchInfoEncryptLogInfo.DoesNotExist:  | 
            |
| 221 | 
                + return response()  | 
            |
| 222 | 
                +  | 
            |
| 223 | 
                + plaintext = miel.plaintext  | 
            |
| 224 | 
                +  | 
            |
| 225 | 
                + # brand_id#model_id#distributor_id#sn#time  | 
            |
| 226 | 
                + # AAAA#AAAAAA#AAAAA#AAAAAAAAAAAAAA#180224  | 
            |
| 227 | 
                +    brand_pk, model_pk, distributor_pk, sn, time = plaintext.split('#')
               | 
            |
| 228 | 
                +  | 
            |
| 229 | 
                + try:  | 
            |
| 230 | 
                + brand = BrandInfo.objects.get(pk=brand_pk)  | 
            |
| 231 | 
                + except BrandInfo.DoesNotExist:  | 
            |
| 232 | 
                + brand = None  | 
            |
| 233 | 
                +  | 
            |
| 234 | 
                + try:  | 
            |
| 235 | 
                + model = ModelInfo.objects.get(pk=model_pk)  | 
            |
| 236 | 
                + except ModelInfo.DoesNotExist:  | 
            |
| 237 | 
                + model = None  | 
            |
| 238 | 
                +  | 
            |
| 239 | 
                +    mdli, created_at = MchInfoDecryptLogInfo.objects.get_or_create(ciphertext=ciphertext, defaults={
               | 
            |
| 240 | 
                + 'brand_pk': brand_pk,  | 
            |
| 241 | 
                + 'model_pk': model_pk,  | 
            |
| 242 | 
                + 'distributor_pk': distributor_pk,  | 
            |
| 243 | 
                + 'sn': sn,  | 
            |
| 244 | 
                + 'decrypt_count': 1,  | 
            |
| 245 | 
                + })  | 
            |
| 246 | 
                +  | 
            |
| 247 | 
                + if not created_at:  | 
            |
| 248 | 
                + mdli.decrypt_count += 1  | 
            |
| 249 | 
                + mdli.save()  | 
            |
| 250 | 
                +  | 
            |
| 251 | 
                +    act = ActivityInfo.objects.filter(status=True).order_by('-pk').first()
               | 
            |
| 252 | 
                + has_unexpired_activity = True if act and act.has_unexpired_activity(model.model_uni_name) else False  | 
            |
| 253 | 
                +  | 
            |
| 254 | 
                +    coupon_info = {
               | 
            |
| 255 | 
                + 'coupon_expire_at': act.final_coupon_expire_at(created_at=None),  | 
            |
| 256 | 
                + 'coupon_value': act.coupon_value,  | 
            |
| 257 | 
                +    } if has_unexpired_activity else {
               | 
            |
| 258 | 
                + 'coupon_expire_at': '',  | 
            |
| 259 | 
                + 'coupon_value': 0,  | 
            |
| 260 | 
                + }  | 
            |
| 261 | 
                +  | 
            |
| 262 | 
                +    return response(200, data={
               | 
            |
| 263 | 
                + 'plaintext': plaintext,  | 
            |
| 264 | 
                + 'logo_url': brand.brand_logo_url if brand else '',  | 
            |
| 265 | 
                + 'model_imgs': model.images if model else [],  | 
            |
| 266 | 
                +        'goodsInfo': {
               | 
            |
| 267 | 
                + 'BrandID': brand_pk,  | 
            |
| 268 | 
                + 'Brand': brand.brand_name if brand else '',  | 
            |
| 269 | 
                + 'ModelID': model_pk,  | 
            |
| 270 | 
                + 'Model': (model.model_full_name or model.model_name) if model else '',  | 
            |
| 271 | 
                + 'DistributorID': distributor_pk,  | 
            |
| 272 | 
                + 'SerialNo': sn,  | 
            |
| 273 | 
                + },  | 
            |
| 274 | 
                + 'has_unexpired_activity': has_unexpired_activity,  | 
            |
| 275 | 
                + 'coupon_info': coupon_info,  | 
            |
| 276 | 
                + })  | 
            
                @@ -214,6 +214,7 @@ urlpatterns += [  | 
            ||
| 214 | 214 | 
                urlpatterns += [  | 
            
| 215 | 215 | 
                url(r'^encrypt$', encrypt_views.encrypt, name='encrypt'),  | 
            
| 216 | 216 | 
                url(r'^decrypt$', encrypt_views.decrypt, name='decrypt'),  | 
            
| 217 | 
                + url(r'^decrypt2$', encrypt_views.decrypt2, name='decrypt2'),  | 
            |
| 217 | 218 | 
                ]  | 
            
| 218 | 219 | 
                 | 
            
| 219 | 220 | 
                urlpatterns += [  | 
            
                @@ -57,6 +57,7 @@ INSTALLED_APPS = (  | 
            ||
| 57 | 57 | 
                'guideline',  | 
            
| 58 | 58 | 
                'integral',  | 
            
| 59 | 59 | 
                'logs',  | 
            
| 60 | 
                + 'marketcode',  | 
            |
| 60 | 61 | 
                'mch',  | 
            
| 61 | 62 | 
                'message',  | 
            
| 62 | 63 | 
                'miniapp',  | 
            
                @@ -252,12 +253,12 @@ QINIU = {
               | 
            ||
| 252 | 253 | 
                'secret_key': '05sCekniLCgM6-d_PxrH8sFjvEOsx3ev-FgS7R-k',  | 
            
| 253 | 254 | 
                'bucket_default': 'photo',  | 
            
| 254 | 255 | 
                     'buckets': {
               | 
            
| 255 | 
                - 'original': 'http://orf3sfb8s.bkt.clouddn.com',  | 
            |
| 256 | 
                - 'photo': 'http://orf3lnlmb.bkt.clouddn.com',  | 
            |
| 257 | 
                - 'prettify': 'http://orzfu8zxw.bkt.clouddn.com',  | 
            |
| 258 | 
                - 'thumbnail': 'http://orf3ahvt6.bkt.clouddn.com',  | 
            |
| 259 | 
                - 'thumbnail2': 'http://orf3muf5n.bkt.clouddn.com',  | 
            |
| 260 | 
                - 'watermark': 'http://orf3qne9f.bkt.clouddn.com',  | 
            |
| 256 | 
                + 'original': 'http://original.img.pai.ai',  | 
            |
| 257 | 
                + 'photo': 'http://photo.img.pai.ai',  | 
            |
| 258 | 
                + 'prettify': 'http://prettify.img.pai.ai',  | 
            |
| 259 | 
                + 'thumbnail': 'http://thumbnail.img.pai.ai',  | 
            |
| 260 | 
                + 'thumbnail2': 'http://thumbnail2.img.pai.ai',  | 
            |
| 261 | 
                + 'watermark': 'http://watermark.img.pai.ai',  | 
            |
| 261 | 262 | 
                }  | 
            
| 262 | 263 | 
                }  | 
            
| 263 | 264 | 
                 | 
            
                @@ -419,6 +420,16 @@ PHONE_2_ADMINISTRATIVE_DIVISION = 'https://www.baifubao.com/callback?cmd=1059&ca  | 
            ||
| 419 | 420 | 
                 | 
            
| 420 | 421 | 
                TESTING_SNS = ['000000']  | 
            
| 421 | 422 | 
                 | 
            
| 423 | 
                +COMPONENT_CALLBACK_CONFIG = {
               | 
            |
| 424 | 
                + 'tousername': 'brand_id',  | 
            |
| 425 | 
                +}  | 
            |
| 426 | 
                +  | 
            |
| 427 | 
                +# 测试文件  | 
            |
| 428 | 
                +TESTING_ZBAR = os.path.join(BASE_DIR, 'utils/zbar/zbar.jpg').replace('\\', '/')
               | 
            |
| 429 | 
                +  | 
            |
| 430 | 
                +# 一物一码设置  | 
            |
| 431 | 
                +KODO_MARKET_CODE_ENABLED = False  | 
            |
| 432 | 
                +  | 
            |
| 422 | 433 | 
                # 开发调试相关配置  | 
            
| 423 | 434 | 
                if DEBUG:  | 
            
| 424 | 435 | 
                try:  | 
            
                @@ -7,7 +7,7 @@ from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo, MchLogInfo  | 
            ||
| 7 | 7 | 
                 | 
            
| 8 | 8 | 
                 | 
            
| 9 | 9 | 
                class MchInfoEncryptLogInfoAdmin(Readonly2ModelAdmin, admin.ModelAdmin):  | 
            
| 10 | 
                -    list_display = ('plaintext', 'alg', 'ciphertext', 'brand_pk', 'model_pk', 'distributor_pk', 'sn', 'operator_id', 'is_send_redpack', 'redpack_amount', 'redpack_max_amount', 'has_send_redpack', 'redpack_send_amount', 'user_id', 'nickname', 'is_clerk_send_redpack', 'clerk_redpack_amount', 'clerk_redpack_max_amount', 'has_clerk_send_redpack', 'clerk_redpack_send_amount', 'clerk_user_id', 'clerk_nickname', 'status', 'created_at', 'updated_at')
               | 
            |
| 10 | 
                +    list_display = ('plaintext', 'alg', 'ciphertext', 'brand_pk', 'model_pk', 'distributor_pk', 'sn', 'code', 'code_url', 'operator_id', 'is_send_redpack', 'redpack_amount', 'redpack_max_amount', 'has_send_redpack', 'redpack_send_amount', 'user_id', 'nickname', 'is_clerk_send_redpack', 'clerk_redpack_amount', 'clerk_redpack_max_amount', 'has_clerk_send_redpack', 'clerk_redpack_send_amount', 'clerk_user_id', 'clerk_nickname', 'status', 'created_at', 'updated_at')
               | 
            |
| 11 | 11 | 
                     list_filter = ('alg', 'brand_pk', 'model_pk', 'distributor_pk', 'operator_id', 'is_send_redpack', 'has_send_redpack', 'is_clerk_send_redpack', 'has_clerk_send_redpack', 'status')
               | 
            
| 12 | 12 | 
                     readonly_fields_exclude = ('is_send_redpack', 'redpack_amount', 'redpack_max_amount', 'is_clerk_send_redpack', 'clerk_redpack_amount', 'clerk_redpack_max_amount')
               | 
            
| 13 | 13 | 
                 | 
            
                @@ -0,0 +1,25 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +# Generated by Django 1.11.28 on 2020-03-22 21:25  | 
            |
| 3 | 
                +from __future__ import unicode_literals  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.db import migrations, models  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class Migration(migrations.Migration):  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                + dependencies = [  | 
            |
| 11 | 
                +        ('logs', '0013_mchinfoencryptloginfo_user_ids'),
               | 
            |
| 12 | 
                + ]  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                + operations = [  | 
            |
| 15 | 
                + migrations.AddField(  | 
            |
| 16 | 
                + model_name='mchinfoencryptloginfo',  | 
            |
| 17 | 
                + name='code',  | 
            |
| 18 | 
                + field=models.CharField(blank=True, db_index=True, help_text='\u4e5d\u4f4d\u7684\u5b57\u7b26\u4e32\u539f\u59cb\u7801', max_length=16, null=True, verbose_name='code'),  | 
            |
| 19 | 
                + ),  | 
            |
| 20 | 
                + migrations.AddField(  | 
            |
| 21 | 
                + model_name='mchinfoencryptloginfo',  | 
            |
| 22 | 
                + name='code_url',  | 
            |
| 23 | 
                + field=models.CharField(blank=True, db_index=True, help_text='28\u4f4d\u666e\u901a\u7801\u5b57\u7b26\t', max_length=128, null=True, verbose_name='code_url'),  | 
            |
| 24 | 
                + ),  | 
            |
| 25 | 
                + ]  | 
            
                @@ -18,6 +18,10 @@ class MchInfoEncryptLogInfo(BaseModelMixin):  | 
            ||
| 18 | 18 | 
                 | 
            
| 19 | 19 | 
                sn = models.CharField(_(u'sn'), max_length=32, blank=True, null=True, help_text=u'序列号', db_index=True)  | 
            
| 20 | 20 | 
                 | 
            
| 21 | 
                + # 一物一码  | 
            |
| 22 | 
                + code = models.CharField(_(u'code'), max_length=16, blank=True, null=True, help_text=u'九位的字符串原始码', db_index=True)  | 
            |
| 23 | 
                + code_url = models.CharField(_(u'code_url'), max_length=128, blank=True, null=True, help_text=u'28位普通码字符 ', db_index=True)  | 
            |
| 24 | 
                +  | 
            |
| 21 | 25 | 
                operator_id = models.CharField(_(u'operator_id'), max_length=32, blank=True, null=True, help_text=u'操作员唯一标识', db_index=True)  | 
            
| 22 | 26 | 
                 | 
            
| 23 | 27 | 
                is_send_redpack = models.BooleanField(_(u'is_send_redpack'), default=False, help_text=u'是否发放红包', db_index=True)  | 
            
                @@ -0,0 +1,12 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.contrib import admin  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from marketcode.models import MarketCodeInfo  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class MarketCodeInfoAdmin(admin.ModelAdmin):  | 
            |
| 9 | 
                +    list_display = ('isv_application_id', 'application_id', 'code', 'code_url', 'has_used', 'status', 'created_at', 'updated_at')
               | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                +admin.site.register(MarketCodeInfo, MarketCodeInfoAdmin)  | 
            
                @@ -0,0 +1,8 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.apps import AppConfig  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class MarketcodeConfig(AppConfig):  | 
            |
| 8 | 
                + name = 'marketcode'  | 
            
                @@ -0,0 +1,34 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +# Generated by Django 1.11.26 on 2020-01-13 10:32  | 
            |
| 3 | 
                +from __future__ import unicode_literals  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.db import migrations, models  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class Migration(migrations.Migration):  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                + initial = True  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + dependencies = [  | 
            |
| 13 | 
                + ]  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                + operations = [  | 
            |
| 16 | 
                + migrations.CreateModel(  | 
            |
| 17 | 
                + name='MarketCodeInfo',  | 
            |
| 18 | 
                + fields=[  | 
            |
| 19 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 20 | 
                +                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 21 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 22 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 23 | 
                +                ('isv_application_id', models.CharField(blank=True, db_index=True, help_text='\u5916\u90e8\u5355\u53f7', max_length=128, null=True, verbose_name='isv_application_id')),
               | 
            |
| 24 | 
                +                ('application_id', models.IntegerField(db_index=True, default=0, help_text='\u7533\u8bf7\u5355\u53f7', verbose_name='application_id')),
               | 
            |
| 25 | 
                +                ('code', models.CharField(blank=True, db_index=True, help_text='\u4e5d\u4f4d\u7684\u5b57\u7b26\u4e32\u539f\u59cb\u7801', max_length=16, null=True, verbose_name='code')),
               | 
            |
| 26 | 
                +                ('code_url', models.CharField(blank=True, db_index=True, help_text='28\u4f4d\u666e\u901a\u7801\u5b57\u7b26\t', max_length=128, null=True, verbose_name='code_url')),
               | 
            |
| 27 | 
                +                ('has_used', models.BooleanField(db_index=True, default=True, help_text='\u662f\u5426\u5df2\u4f7f\u7528', verbose_name='has_used')),
               | 
            |
| 28 | 
                + ],  | 
            |
| 29 | 
                +            options={
               | 
            |
| 30 | 
                + 'verbose_name': '\u4e00\u7269\u4e00\u7801\u4fe1\u606f',  | 
            |
| 31 | 
                + 'verbose_name_plural': '\u4e00\u7269\u4e00\u7801\u4fe1\u606f',  | 
            |
| 32 | 
                + },  | 
            |
| 33 | 
                + ),  | 
            |
| 34 | 
                + ]  | 
            
                @@ -0,0 +1,20 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +# Generated by Django 1.11.26 on 2020-01-13 10:51  | 
            |
| 3 | 
                +from __future__ import unicode_literals  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.db import migrations, models  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class Migration(migrations.Migration):  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                + dependencies = [  | 
            |
| 11 | 
                +        ('marketcode', '0001_initial'),
               | 
            |
| 12 | 
                + ]  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                + operations = [  | 
            |
| 15 | 
                + migrations.AlterField(  | 
            |
| 16 | 
                + model_name='marketcodeinfo',  | 
            |
| 17 | 
                + name='has_used',  | 
            |
| 18 | 
                + field=models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u5df2\u4f7f\u7528', verbose_name='has_used'),  | 
            |
| 19 | 
                + ),  | 
            |
| 20 | 
                + ]  | 
            
                @@ -0,0 +1,25 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +# Generated by Django 1.11.26 on 2020-01-13 18:20  | 
            |
| 3 | 
                +from __future__ import unicode_literals  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.db import migrations, models  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class Migration(migrations.Migration):  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                + dependencies = [  | 
            |
| 11 | 
                +        ('marketcode', '0002_auto_20200113_1851'),
               | 
            |
| 12 | 
                + ]  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                + operations = [  | 
            |
| 15 | 
                + migrations.AddField(  | 
            |
| 16 | 
                + model_name='marketcodeinfo',  | 
            |
| 17 | 
                + name='code_index',  | 
            |
| 18 | 
                + field=models.IntegerField(default=0, help_text='\u8be5\u7801\u5728\u6279\u6b21\u4e2d\u7684\u504f\u79fb\u91cf\t', verbose_name='code_index'),  | 
            |
| 19 | 
                + ),  | 
            |
| 20 | 
                + migrations.AddField(  | 
            |
| 21 | 
                + model_name='marketcodeinfo',  | 
            |
| 22 | 
                + name='lattice',  | 
            |
| 23 | 
                + field=models.CharField(blank=True, help_text='361 \u5b57\u8282\u7684 01 \u70b9\u9635\uff0c\u7528\u4e8e\u652f\u6301\u751f\u6210 19 * 19 \u7684\u5fae\u578b\u7801\uff0c0 \u4e3a\u767d\uff0c1 \u4e3a\u9ed1', max_length=361, null=True, verbose_name='code'),  | 
            |
| 24 | 
                + ),  | 
            |
| 25 | 
                + ]  | 
            
                @@ -0,0 +1,24 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import models  | 
            |
| 4 | 
                +from django.utils.translation import ugettext_lazy as _  | 
            |
| 5 | 
                +from django_models_ext import BaseModelMixin  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class MarketCodeInfo(BaseModelMixin):  | 
            |
| 9 | 
                + isv_application_id = models.CharField(_(u'isv_application_id'), max_length=128, blank=True, null=True, help_text=u'外部单号', db_index=True)  | 
            |
| 10 | 
                + application_id = models.IntegerField(_(u'application_id'), default=0, help_text=u'申请单号', db_index=True)  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + lattice = models.CharField(_(u'code'), max_length=361, blank=True, null=True, help_text=u'361 字节的 01 点阵,用于支持生成 19 * 19 的微型码,0 为白,1 为黑')  | 
            |
| 13 | 
                + code = models.CharField(_(u'code'), max_length=16, blank=True, null=True, help_text=u'九位的字符串原始码', db_index=True)  | 
            |
| 14 | 
                + code_index = models.IntegerField(_(u'code_index'), default=0, help_text=u'该码在批次中的偏移量 ')  | 
            |
| 15 | 
                + code_url = models.CharField(_(u'code_url'), max_length=128, blank=True, null=True, help_text=u'28位普通码字符 ', db_index=True)  | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                + has_used = models.BooleanField(_(u'has_used'), default=False, help_text=_(u'是否已使用'), db_index=True)  | 
            |
| 18 | 
                +  | 
            |
| 19 | 
                + class Meta:  | 
            |
| 20 | 
                + verbose_name = _(u'一物一码信息')  | 
            |
| 21 | 
                + verbose_name_plural = _(u'一物一码信息')  | 
            |
| 22 | 
                +  | 
            |
| 23 | 
                + def __unicode__(self):  | 
            |
| 24 | 
                + return unicode(self.pk)  | 
            
                @@ -0,0 +1,6 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.test import TestCase  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +# Create your tests here.  | 
            
                @@ -0,0 +1,6 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.shortcuts import render  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +# Create your views here.  | 
            
                @@ -0,0 +1,41 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.conf import settings  | 
            |
| 4 | 
                +from pywe_marketcode import applycodedownload  | 
            |
| 5 | 
                +from pywe_storage import RedisStorage  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +from marketcode.models import MarketCodeInfo  | 
            |
| 8 | 
                +from utils.redis.connect import r  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                +  | 
            |
| 11 | 
                +WECHAT = settings.WECHAT  | 
            |
| 12 | 
                +  | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                +def marketcodedownload(application_id, code_start, code_end, isv_application_id=''):  | 
            |
| 15 | 
                +    wxcfg = WECHAT.get('JSAPI', {})
               | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                +    appid = wxcfg.get('appID')
               | 
            |
| 18 | 
                +    secret = wxcfg.get('appsecret')
               | 
            |
| 19 | 
                +  | 
            |
| 20 | 
                + codes = applycodedownload(application_id=application_id, code_start=code_start, code_end=code_end, appid=appid, secret=secret, token=None, storage=RedisStorage(r))  | 
            |
| 21 | 
                +  | 
            |
| 22 | 
                + lattice, code, code_index, code_url = ''  | 
            |
| 23 | 
                + for idx, item in enumerate(codes):  | 
            |
| 24 | 
                + if idx % 4 == 0:  | 
            |
| 25 | 
                + lattice = item  | 
            |
| 26 | 
                + if idx % 4 == 1:  | 
            |
| 27 | 
                + code = item  | 
            |
| 28 | 
                + if idx % 4 == 2:  | 
            |
| 29 | 
                + code_index = item  | 
            |
| 30 | 
                + if idx % 4 == 3:  | 
            |
| 31 | 
                + code_url = item  | 
            |
| 32 | 
                + MarketCodeInfo.objects.create(  | 
            |
| 33 | 
                + isv_application_id=isv_application_id,  | 
            |
| 34 | 
                + application_id=application_id,  | 
            |
| 35 | 
                + lattice=lattice,  | 
            |
| 36 | 
                + code=code,  | 
            |
| 37 | 
                + code_index=code_index,  | 
            |
| 38 | 
                + code_url=code_url  | 
            |
| 39 | 
                + )  | 
            |
| 40 | 
                +  | 
            |
| 41 | 
                +  | 
            
                @@ -2,6 +2,7 @@ pywe-card==1.0.0  | 
            ||
| 2 | 2 | 
                pywe-component==1.0.1  | 
            
| 3 | 3 | 
                pywe-component-preauthcode==1.0.3  | 
            
| 4 | 4 | 
                pywe-jssdk==1.1.0  | 
            
| 5 | 
                +pywe-marketcode==1.0.1  | 
            |
| 5 | 6 | 
                pywe-membercard==1.0.3  | 
            
| 6 | 7 | 
                pywe-miniapp==1.1.5  | 
            
| 7 | 8 | 
                pywe-oauth==1.0.7  | 
            
                @@ -59,6 +59,39 @@ class ProductStatusCode(BaseStatusCode):  | 
            ||
| 59 | 59 | 
                PRODUCT_NOT_USED = StatusCodeField(502012, 'Product Not Used', description=u'产品未使用')  | 
            
| 60 | 60 | 
                 | 
            
| 61 | 61 | 
                 | 
            
| 62 | 
                +class MemberGoodStatusCode(BaseStatusCode):  | 
            |
| 63 | 
                + """ 会员商品相关错误码 5035xx """  | 
            |
| 64 | 
                + GOOD_NOT_FOUND = StatusCodeField(503501, 'Good Not Found', description=u'商品不存在')  | 
            |
| 65 | 
                +  | 
            |
| 66 | 
                + GOOD_NO_EXCHANGE_PERMISSION = StatusCodeField(503502, 'Good No Exchange Permission', description=u'商品无兑换权限')  | 
            |
| 67 | 
                + GOOD_INTEGRAL_NOT_ENOUGH = StatusCodeField(503503, 'Good Integral Not Enough', description=u'商品兑换积分不足')  | 
            |
| 68 | 
                + GOOD_STOCK_NOT_ENOUGH = StatusCodeField(503504, 'Good Integral Not Enough', description=u'商品库存不足')  | 
            |
| 69 | 
                +  | 
            |
| 70 | 
                +  | 
            |
| 71 | 
                +class MemberRightStatusCode(BaseStatusCode):  | 
            |
| 72 | 
                + """ 会员商品相关错误码 5036xx """  | 
            |
| 73 | 
                + RIGHT_NOT_FOUND = StatusCodeField(503601, 'Right Not Found', description=u'权益不存在')  | 
            |
| 74 | 
                +  | 
            |
| 75 | 
                +  | 
            |
| 76 | 
                +class MemberActivityStatusCode(BaseStatusCode):  | 
            |
| 77 | 
                + """ 会员活动相关错误码 5037xx """  | 
            |
| 78 | 
                + ACTIVITY_NOT_FOUND = StatusCodeField(503701, 'Activity Not Found', description=u'活动不存在')  | 
            |
| 79 | 
                +  | 
            |
| 80 | 
                +  | 
            |
| 81 | 
                +class MemberCouponStatusCode(BaseStatusCode):  | 
            |
| 82 | 
                + """ 会员优惠券相关错误码 5040xx """  | 
            |
| 83 | 
                + USER_COUPON_NOT_FOUND = StatusCodeField(504001, 'User Coupon Not Found', description=u'用户优惠券不存在')  | 
            |
| 84 | 
                +  | 
            |
| 85 | 
                + USER_COUPON_HAS_USED = StatusCodeField(504010, 'User Coupon Has Used', description=u'用户优惠券已使用')  | 
            |
| 86 | 
                + USER_COUPON_NOT_ACTIVED = StatusCodeField(504011, 'User Coupon Not Actived', description=u'用户优惠券未生效')  | 
            |
| 87 | 
                + USER_COUPON_HAS_EXPIRED = StatusCodeField(504012, 'User Coupon Has Expired', description=u'用户优惠券已过期')  | 
            |
| 88 | 
                +  | 
            |
| 89 | 
                +  | 
            |
| 90 | 
                +class MarketCodeStatusCode(BaseStatusCode):  | 
            |
| 91 | 
                + """ 一物一码相关错误码 5050xx """  | 
            |
| 92 | 
                + MARKET_CODE_NOT_FOUND = StatusCodeField(505001, 'Market Code Not Found', description=u'一物一码不存在')  | 
            |
| 93 | 
                +  | 
            |
| 94 | 
                +  | 
            |
| 62 | 95 | 
                class LensmanStatusCode(BaseStatusCode):  | 
            
| 63 | 96 | 
                """ 摄影师相关错误码 4000xx """  | 
            
| 64 | 97 | 
                LENSMAN_NOT_FOUND = StatusCodeField(400001, 'Lensman Not Found', description=u'摄影师不存在')  |